﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Lhz.Data.Linq
{
    public abstract class LinqProxy : ILinqProxy
    {
        public abstract IQueryable<TEntity> Query<TEntity>();

        protected abstract string[] GetPkColumns<TEntity>();

        public virtual TEntity QueryByPk<TPk, TEntity>(TPk pk)
        {
            var typeEntity = typeof(TEntity);
            var pkColumns = GetPkColumns<TEntity>();
            if (pkColumns.Length == 0)
            {
                throw new NotSupportedException("该实体【" + typeEntity.FullName + "】不包含主键，不支持此方法。");
            }
            else
            {
                var typePk = pk.GetType();
                var parameter = Expression.Parameter(typeEntity, "r");
                Expression expression = null;
                if (pkColumns.Length == 1)
                {
                    var pkColumn = pkColumns.First();
                    var pi = typeEntity.GetProperty(pkColumn);
                    if (pi.PropertyType == typePk)
                    {
                        expression = Expression.Equal(Expression.Property(parameter, typeEntity.GetProperty(pkColumn)), Expression.Constant(pk));
                    }
                    else
                    {
                        throw new ArgumentException("传入主键对象的类型【" + typePk.FullName + "】与实体主键属性的类型【" + pi.PropertyType.FullName + "】不匹配。");
                    }
                }
                else
                {
                    foreach (var pkColumn in pkColumns)
                    {
                        var pkPropertyInfo = typePk.GetProperty(pkColumn);
                        if (pkPropertyInfo == null)
                        {
                            throw new ArgumentException("传入的主键对象缺少主键属性【" + pkColumn + "】。");
                        }
                        var entityPropertyInfo = typeEntity.GetProperty(pkColumn);
                        var pkPropertyValue = pkPropertyInfo.GetValue(pk);
                        var pkPropertyValueType = pkPropertyValue.GetType();
                        if (entityPropertyInfo.PropertyType != pkPropertyValueType)
                        {
                            throw new ArgumentException("传入的主键对象中，【" + pkColumn + "】属性的类型【" + pkPropertyValueType.FullName + "】与实体中的类型【" + entityPropertyInfo.PropertyType.FullName + "】不匹配。");
                        }
                        var childExpression = Expression.Equal(Expression.Property(parameter, entityPropertyInfo), Expression.Constant(pkPropertyValue));
                        if (expression == null)
                        {
                            expression = childExpression;
                        }
                        else
                        {
                            expression = Expression.And(expression, childExpression);
                        }
                    }
                }
                var query = Query<TEntity>();
                return query.Provider.CreateQuery<TEntity>(Expression.Call(typeof(Queryable), "Where", new Type[] { typeEntity }, query.Expression, Expression.Lambda(expression, parameter))).ToList().FirstOrDefault();
            }
        }

        public abstract void ClearCache();

        public abstract void Add<TEntity>(TEntity item);

        public virtual void Add<TEntity>(IEnumerable<TEntity> items)
        {
            foreach (var item in items)
            {
                Add(item);
            }
        }

        public abstract void Update<TEntity>(TEntity item) where TEntity : class;

        public virtual void Update<TEntity>(IEnumerable<TEntity> items) where TEntity : class
        {
            foreach (var item in items)
            {
                Update(item);
            }
        }

        public abstract void Delete<TEntity>(TEntity item) where TEntity : class;

        public virtual void Delete<TEntity>(IEnumerable<TEntity> items) where TEntity : class
        {
            foreach (var item in items)
            {
                Delete(item);
            }
        }

        public abstract void DeleteByPk<TPk, TEntity>(TPk pk);

        public virtual void DeleteByPk<TPk, TEntity>(IEnumerable<TPk> pks)
        {
            foreach (var pk in pks)
            {
                DeleteByPk<TPk, TEntity>(pk);
            }
        }

        public abstract void SaveChanges();

        public abstract void Dispose();
    }
}
